import { BufferAttribute, BufferGeometry, Vector3} from "three";
import { Point } from "../types/Point";

/** 自定义平面几何体 */
export default class CustomPlaneGeometry extends BufferGeometry {    
    public earClipData;

    constructor(points: Array<Point>, isComputeTexCoord: boolean){
        super();

        this.earClipData = this.earClipping2D(points, isComputeTexCoord); //耳切法得到数据
        if(this.earClipData.pointArray == null){
            console.log('多边形顶点数据不合法，无法构造多边形');
            return;
        }
        const vertices = new Float32Array(this.earClipData.pointArray);
        const uvArray = new Float32Array(this.earClipData.uvCoordArray);
        this.setAttribute( 'position', new BufferAttribute( vertices, 3 ) );
        this.setAttribute( 'uv', new BufferAttribute( uvArray, 2 ) );
        this.computeVertexNormals();
    }

      /**
     * 用耳切法计算三角形顶点数组
     * @param points 顶点数组
     * @param isComputeTexCoord 是否计算纹理坐标
     * @returns 
     */
      private earClipping2D(points: Array<Point>, isComputeTexCoord: boolean): { pointArray: Array<number>, uvCoordArray: Array<number>, isCCW: boolean, width: number, height: number, minX: number, minY: number, firstPoint: Vector3 } {
        let bPolygonIsConvex: Array<boolean> = new Array<boolean>();//对所有polygon点信息，凹凸信息进行标定 
        let finalNormal: Vector3 = new Vector3();
        let finalDirection = 0;
        finalNormal.x = 0;
        finalNormal.y = 0;
        finalNormal.z = 0;

        for (let i = 0; i < points.length; ++i) {//判断顶点是顺时针还是逆时针
            let curPos: Vector3 = points[i].toEPSGWeb(); //new Vector3(points[i][0], points[i][1], 0);
            let leftPos: Vector3;
            let rightPos: Vector3;

            if (i == 0) {
                let iEnd = points.length;
                leftPos = points[iEnd - 1].toEPSGWeb(); //new Vector3(points.at(iEnd-1)[0], points.at(iEnd-1)[1], 0);
            } else {
                leftPos = points[i - 1].toEPSGWeb(); //new Vector3(points[i - 1][0], points[i - 1][1], 0);
            }

            if (i == (points.length - 1)) {
                rightPos = points[0].toEPSGWeb(); //new Vector3(points.at(0)[0], points.at(0)[1], 0);
            } else {
                rightPos = points[i + 1].toEPSGWeb(); //new Vector3(points[i + 1][0], points[i + 1][1], 0);
            }

            let tempOne: Vector3 = curPos.clone().sub(leftPos);
            let vectorOne: Vector3 = new Vector3();
            vectorOne.x = tempOne.x;
            vectorOne.y = tempOne.y;
            vectorOne.z = 0;

            let tempTwo = rightPos.clone().sub(curPos);
            let vectorTwo: Vector3 = new Vector3();
            vectorTwo.x = tempTwo.x;
            vectorTwo.y = tempTwo.y;
            vectorTwo.z = 0;

            //向量叉乘
            let currentNormal: Vector3 = vectorOne.cross(vectorTwo).normalize();
            if (currentNormal.z > 0) {
                finalDirection += 1;
                bPolygonIsConvex.push(true);
            } else if (currentNormal.z < 0) {
                finalDirection -= 1;
                bPolygonIsConvex.push(false);
            } else if (currentNormal.z == 0) {
                bPolygonIsConvex.push(false);
            }
        }

        let isCCW = (finalDirection > 0) ? true : false;
        let returnData: { pointArray: Array<number>, uvCoordArray: Array<number>, isCCW: boolean, width: number, height: number, minX: number, minY: number, firstPoint: Vector3 } =
            { pointArray: null, uvCoordArray: null, isCCW: isCCW, width: 0, height: 0, minX: 0, minY: 0, firstPoint: null };
        let pointArray = new Array<number>();
        let minX, minY, maxX, maxY;
        let firstPoint: Vector3;
        // let triangles: Array<Vector3> = new Array<Vector3>();
        if (points.length < 3) {
            return returnData;
        } else {
            let earFound = true;
            let lastSize = 0;
            while ((points.length >= 3)) {
                if ((points.length == lastSize)) {
                    finalDirection = finalDirection * -1;
                }
                lastSize = points.length;
                if (!earFound) {
                    return returnData;
                }

                for (let i = 0; i < points.length; i++) {
                    let curPos: Vector3 = points[i]; //new Point(points[i][0], points[i][1], 0, '3857').toEPSGWeb();
                    let leftPos: Vector3;
                    let rightPos: Vector3;
                    if (i == 0) {
                        let iEnd = points.length;
                        leftPos = points[iEnd - 1]; //new Point(points.at(iEnd-1)[0], points.at(iEnd-1)[1], 0, '3857').toEPSGWeb();
                    } else {
                        leftPos = points[i - 1]; //new Point(points[i - 1][0], points[i - 1][1], 0, '3857').toEPSGWeb();
                    }
                    if (i == points.length - 1) {
                        rightPos = points[0]; //new Point(points.at(0)[0], points.at(0)[1], 0, '3857').toEPSGWeb();
                    } else {
                        rightPos = points[i + 1]; //new Point(points[i + 1][0], points[i + 1][1], 0, '3857').toEPSGWeb();
                    }

                    let isEar = true;
                    let isConvex = true;

                    let tempOne = curPos.clone().sub(leftPos);
                    let vectorOne: Vector3 = new Vector3();
                    vectorOne.x = tempOne.x;
                    vectorOne.y = tempOne.y;
                    vectorOne.z = 0;

                    let tempTwo = rightPos.clone().sub(curPos);
                    let vectorTwo: Vector3 = new Vector3();
                    vectorTwo.x = tempTwo.x;
                    vectorTwo.y = tempTwo.y;
                    vectorTwo.z = 0;

                    let currentNormal = vectorOne.cross(vectorTwo);
                    currentNormal = currentNormal.normalize();

                    if (finalDirection > 0) { //逆时针 
                        if (currentNormal.z < 0) {
                            //法线往下，就说明这是一个凹点
                            isConvex = false;
                        }
                    }
                    else if (finalDirection < 0) { //顺时针
                        if (currentNormal.z > 0) {
                            //法线往上，就说明这是一个凹点
                            isConvex = false;
                        }
                    }

                    if (isConvex == true) {
                        let lastNormal: Vector3;
                        let currentTriangle: Array<Vector3> = new Array<Vector3>();//当前的三角形
                        currentTriangle.push(leftPos);
                        currentTriangle.push(curPos);
                        currentTriangle.push(rightPos);
                        currentTriangle.push(leftPos);

                        for (let j = 0; j < points.length; j++) {
                            if (!isEar) {
                                break;
                            }

                            //let currPoint:Vector3 = new Vector3(points[j][0], points[j][1], 0);
                            let currPoint: Vector3 = new Vector3(points[j].x, points[j].y, 0);
                            let isCheck = true;
                            for (let m = 0; m < currentTriangle.length; m++) {
                                let currTriganglePoint: Vector3 = currentTriangle[m];
                                let tempOne = currPoint.clone().sub(currTriganglePoint);
                                if (tempOne.x == 0 && tempOne.y == 0) {
                                    isCheck = false;
                                    break;
                                }
                            }

                            if (isCheck == true) {
                                let sum = 1;
                                let hasNormal = false;
                                for (let k = 0; k < currentTriangle.length - 1; k++) {
                                    let currTriganglePoint: Vector3 = currentTriangle[k];
                                    let nextTriganglePoint: Vector3 = currentTriangle[k + 1];

                                    let tempOne: Vector3 = currPoint.clone().sub(currTriganglePoint);
                                    let vectorOne: Vector3 = new Vector3();
                                    vectorOne.x = tempOne.x;
                                    vectorOne.y = tempOne.y;
                                    vectorOne.z = 0;

                                    let tempTwo: Vector3 = nextTriganglePoint.clone().sub(currTriganglePoint);
                                    let vectorTwo: Vector3 = new Vector3();
                                    vectorTwo.x = tempTwo.x;
                                    vectorTwo.y = tempTwo.y;
                                    vectorTwo.z = 0;

                                    let currentNormal: Vector3 = vectorOne.cross(vectorTwo);
                                    if (hasNormal) {
                                        let direct = lastNormal.z * currentNormal.z;
                                        if (direct > 0) {
                                            sum++;
                                        } else {
                                            sum--;
                                        }
                                    }
                                    hasNormal = true;
                                    lastNormal = currentNormal;
                                }

                                if (sum < 3) { //没有被三角形包含
                                    isEar = true;
                                } else if (sum >= 3) {//被三角形包含
                                    isEar = false;
                                    break;
                                }
                            }
                        }
                    } else {
                        isEar = false;
                        if (points.length == 3) {
                            earFound = false;
                        }
                    }

                    if (isEar && points.length >= 3) {
                        let currentMinX = Math.min(leftPos.x, curPos.x, rightPos.x);
                        let currentMinY = Math.min(leftPos.y, curPos.y, rightPos.y);
                        let currentMaxX = Math.max(leftPos.x, curPos.x, rightPos.x);
                        let currentMaxY = Math.max(leftPos.y, curPos.y, rightPos.y);

                        if (pointArray.length == 0) {
                            minX = currentMinX;
                            minY = currentMinY;
                            maxX = currentMaxX;
                            maxY = currentMaxY;

                            firstPoint = leftPos.clone();
                        } else {
                            if (currentMinX < minX) {
                                minX = currentMinX;
                            }
                            if (currentMinY < minY) {
                                minY = currentMinY;
                            }
                            if (currentMaxX > maxX) {
                                maxX = currentMaxX;
                            }
                            if (currentMaxY > maxY) {
                                maxY = currentMaxY;
                            }
                        }

                        pointArray.push(leftPos.x - firstPoint.x, leftPos.y - firstPoint.y, leftPos.z - firstPoint.z);
                        pointArray.push(curPos.x - firstPoint.x, curPos.y - firstPoint.y, curPos.z - firstPoint.z);
                        pointArray.push(rightPos.x - firstPoint.x, rightPos.y - firstPoint.y, rightPos.z - firstPoint.z);

                        let bufferArray: Array<Point> = new Array<Point>();
                        for (let n = 0; n < points.length; n++) { //把数组复制到新的数组中，为了不破坏原始数据
                            if (n != i) {
                                bufferArray.push(points[n]);
                            }
                        }
                        points = bufferArray;
                        i = i - 1;
                    }
                }
            }
        }

        returnData.width = maxX - minX;
        returnData.height = maxY - minY;
        returnData.minX = minX;
        returnData.minY = minY;
        returnData.pointArray = pointArray;
        returnData.firstPoint = firstPoint;

        let uvCoordArray = [];
        if (isComputeTexCoord) {
            for (let i = 0; i < pointArray.length; i += 3) {
                let u = ((pointArray[i] + firstPoint.x) - minX) / returnData.width;
                let v = ((pointArray[i + 1] + firstPoint.y) - minY) / returnData.height;
                uvCoordArray.push(u);
                uvCoordArray.push(v);
            }
        }
        returnData.uvCoordArray = uvCoordArray;

        return returnData;
    }

}